package com.tianji.promotion.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.plugins.pagination.PageDto;
import com.tianji.common.exceptions.BadRequestException;
import com.tianji.common.exceptions.BizIllegalException;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.promotion.domain.dto.CouponFormDTO;
import com.tianji.promotion.domain.dto.CouponIssueFormDTO;
import com.tianji.promotion.domain.po.Coupon;
import com.tianji.promotion.domain.po.CouponScope;
import com.tianji.promotion.domain.po.UserCoupon;
import com.tianji.promotion.domain.query.CouponQuery;
import com.tianji.promotion.domain.vo.CouponPageVO;
import com.tianji.promotion.domain.vo.CouponVO;
import com.tianji.promotion.enums.CouponStatus;
import com.tianji.promotion.enums.ObtainType;
import com.tianji.promotion.enums.UserCouponStatus;
import com.tianji.promotion.mapper.CouponMapper;
import com.tianji.promotion.service.ICouponScopeService;
import com.tianji.promotion.service.ICouponService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.promotion.service.IExchangeCodeService;
import com.tianji.promotion.service.IUserCouponService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 优惠券的规则信息 服务实现类
 * </p>
 *
 * @author ldn
 * @since 2024-12-13
 */
@Service
@RequiredArgsConstructor
public class CouponServiceImpl extends ServiceImpl<CouponMapper, Coupon> implements ICouponService {
    private final ICouponScopeService couponScopeService;
    private final IExchangeCodeService exchangeCodeService;
    private final IUserCouponService userCouponService;

    /*
     *新增优惠卷
     * */
    @Override
    @Transactional
    public void saveCoupon(CouponFormDTO dto) {
        Coupon coupon = BeanUtils.copyBean(dto, Coupon.class);
        this.save(coupon);
        //判断是否有范围
        if (!dto.getSpecific()) {
            //没范围
            return;
        }
        //有范围，保存范围
        List<Long> scopes = dto.getScopes();
        if (scopes == null) {
            throw new BizIllegalException("优惠券范围不能为空");
        }
        List<CouponScope> couponScopes = scopes.stream()
                .map(scopeId -> CouponScope.builder()
                        .couponId(coupon.getId())
                        .bizId(scopeId)
                        .build())
                .collect(Collectors.toList());
        couponScopeService.saveBatch(couponScopes);
    }

    @Override
    public PageDto<CouponPageVO> queryCouponByPage(CouponQuery query) {
        Page<Coupon> coupons = this.lambdaQuery()
                .like(!StringUtils.isBlank(query.getName()), Coupon::getName, query.getName())
                .eq(query.getStatus() != null, Coupon::getDiscountType, query.getStatus())
                .eq(query.getType() != null, Coupon::getType, query.getType())
                .page(query.toMpPage());
        //封装对象
        PageDto<CouponPageVO> page = new PageDto<>();
        page.setTotal(coupons.getTotal());
        page.setCurrent(coupons.getCurrent());
        page.setRecords(BeanUtils.copyList(coupons.getRecords(), CouponPageVO.class));
        return page;
    }


    /*
     *发放优惠卷
     * */
    @Transactional
    @Override
    public void issueCoupon(Long id, CouponIssueFormDTO dto) {
        if (id == null) {
            throw new BizIllegalException("优惠卷id不能为空");
        }
        Coupon coupon = this.lambdaQuery()
                .eq(Coupon::getId, id)
                .one();
        if (coupon == null) {
            throw new BizIllegalException("优惠卷不存在");
        }
        // 3.判断是否是立刻发放
        LocalDateTime issueBeginTime = dto.getIssueBeginTime();
        LocalDateTime now = LocalDateTime.now();
        boolean isBegin = issueBeginTime == null || !issueBeginTime.isAfter(now);
        // 4.更新优惠券
        // 4.1.拷贝属性到PO
        Coupon c = BeanUtils.copyBean(dto, Coupon.class);
        // 4.2.更新状态
        if (isBegin) {
            c.setStatus(CouponStatus.ISSUING);
            c.setIssueBeginTime(now);
        } else {
            c.setStatus(CouponStatus.UN_ISSUE);
        }
        // 4.3.写入数据库
        updateById(c);
        // 5.1 兑换码生成
        coupon.setIssueBeginTime(c.getIssueBeginTime());
        coupon.setIssueEndTime(c.getIssueEndTime());
        // 6.判断是否需要生成兑换码，优惠券类型必须是兑换码，优惠券状态必须是待发放
        if (coupon.getObtainWay() == ObtainType.ISSUE && coupon.getStatus() == CouponStatus.DRAFT) {
            coupon.setIssueEndTime(c.getIssueEndTime());
            exchangeCodeService.asyncGenerateCode(coupon);
        }
    }

    /*
     *修改优惠卷
     * */
    @Override
    public void updateCoupon(CouponFormDTO dto, Long id) {
        Coupon coupon = this.lambdaQuery()
                .eq(Coupon::getId, id)
                .one();
        if (coupon == null) {
            throw new BizIllegalException("优惠卷不存在");
        }
        if (coupon.getStatus() == CouponStatus.ISSUING ||
                coupon.getStatus() == CouponStatus.FINISHED ||
                coupon.getStatus() == CouponStatus.PAUSE) {
            throw new BizIllegalException("优惠卷已经无法修改");
        }
        Coupon newCoupon = BeanUtils.copyBean(dto, Coupon.class);
        this.updateById(newCoupon);
        // 判断是否限定了范围
        if (!dto.getSpecific()) { // 未限定范围
            return;
        }
        // 限定范围，需要校验dto.scopes
        List<Long> scopes = dto.getScopes();
        if (CollUtil.isEmpty(scopes)) {
            throw new BadRequestException("分类id不能为空");
        }
        // 限定范围，删除原来的范围，重新添加
        couponScopeService.lambdaUpdate()
                .eq(CouponScope::getCouponId, coupon.getId())
                .remove();
        List<CouponScope> couponScopeList = scopes.stream().map(scope ->
                CouponScope.builder()
                        .couponId(newCoupon.getId())
                        .bizId(scope)
                        .build()).collect(Collectors.toList());
        couponScopeService.saveBatch(couponScopeList);
    }

    /**
     * 删除优惠券，需要删除关联的优惠券范围信息
     */
    @Override
    public void deleteCoupon(Long id) {
        Coupon coupon = this.lambdaQuery()
                .eq(Coupon::getId, id)
                .one();
        if (coupon == null) {
            throw new BizIllegalException("优惠卷不存在");
        }
        if (coupon.getStatus() == CouponStatus.ISSUING ||
                coupon.getStatus() == CouponStatus.FINISHED ||
                coupon.getStatus() == CouponStatus.PAUSE) {
            throw new BizIllegalException("优惠卷已经无法删除");
        }
        this.removeById(id);
        couponScopeService.lambdaUpdate()
                .eq(CouponScope::getCouponId, id)
                .remove();
    }

    /*
     * 返回正在发放的优惠卷
     * */
    @Override
    public List<CouponVO> queryIssuingCoupons() {
        // 1.查询发放中的优惠券列表
        List<Coupon> coupons = lambdaQuery()
                .eq(Coupon::getStatus, CouponStatus.ISSUING)
                .eq(Coupon::getObtainWay, ObtainType.PUBLIC)
                .list();
        if (CollUtils.isEmpty(coupons)) {
            return CollUtils.emptyList();
        }
        // 2.统计当前用户已经领取的优惠券的信息
        List<Long> couponIds = coupons.stream().map(Coupon::getId).collect(Collectors.toList());
        // 2.1.查询当前用户已经领取的优惠券的数据
        List<UserCoupon> userCoupons = userCouponService.lambdaQuery()
                .eq(UserCoupon::getUserId, UserContext.getUser())
                .in(UserCoupon::getCouponId, couponIds)
                .list();
        // 2.2.统计当前用户对优惠券的已经领取数量
        Map<Long, Long> issuedMap = userCoupons.stream()
                .collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));
        // 2.3.统计当前用户对优惠券的已经领取并且未使用的数量
        Map<Long, Long> unusedMap = userCoupons.stream()
                .filter(uc -> uc.getStatus() == UserCouponStatus.UNUSED)
                .collect(Collectors.groupingBy(UserCoupon::getCouponId, Collectors.counting()));
        // 3.封装VO结果
        List<CouponVO> list = new ArrayList<>(coupons.size());
        for (Coupon c : coupons) {
            // 3.1.拷贝PO属性到VO
            CouponVO vo = BeanUtils.copyBean(c, CouponVO.class);
            list.add(vo);
            // 3.2.是否可以领取：已经被领取的数量 < 优惠券总数量 && 当前用户已经领取的数量 < 每人限领数量
            vo.setAvailable(
                    c.getIssueNum() < c.getTotalNum()
                            && issuedMap.getOrDefault(c.getId(), 0L) < c.getUserLimit()
            );
            // 3.3.是否可以使用：当前用户已经领取并且未使用的优惠券数量 > 0
            vo.setReceived(unusedMap.getOrDefault(c.getId(), 0L) > 0);
        }
        return list;
    }
}
